package edu.ccut.saturn.component.impl;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.jexl.JexlContext;
import org.apache.commons.jexl.JexlHelper;

import edu.ccut.saturn.component.IActionContext;
import edu.ccut.saturn.component.ILogicContext;
import edu.ccut.saturn.component.IParameterInfo;
import edu.ccut.saturn.component.impl.mock.MockRequest;
import edu.ccut.saturn.component.impl.mock.MockResponse;
import edu.ccut.saturn.component.utils.DateUtils;

/**
 * {@link IActionContext} implementation
 * 
 * @see IActionContext
 * @author LeslieGu
 * 
 */
public class SaturnActionContext extends AbstractSaturnContext implements
		IActionContext {

	private HttpServletRequest request;

	private HttpServletResponse response;

	private Map<String, Object> logicMap;

	private JexlContext context;

	@SuppressWarnings("unchecked")
	public SaturnActionContext(String id, HttpServletRequest request,
			HttpServletResponse response) {

		super(id);
		this.request = request;
		this.response = response;
		this.context = JexlHelper.createContext();
		this.logicMap = context.getVars();
		initalLogicMapByWebContext(this.request);
	}

	public HttpServletRequest getRequest() {
		return request;
	}

	public HttpServletResponse getResponse() {
		return response;
	}

	@Override
	public Object getValue(String key) {
		return this.getValue(key, IParameterInfo.SCOPE_LOGIC);
	}

	@Override
	public void setValue(String key, Object value) {
		this.setValue(key, value, IParameterInfo.SCOPE_LOGIC);
	}

	@Override
	public Object getValue(IParameterInfo parameterInfo) {

		if (parameterInfo != null) {
			return this.getValue(parameterInfo.getValueKey(), parameterInfo
					.getScope());
		}

		return null;
	}

	@Override
	public void setValue(IParameterInfo parameterInfo, Object value) {

		if (parameterInfo != null) {
			this.setValue(parameterInfo.getValueKey(), value, parameterInfo
					.getScope());
		}
	}

	public Object getValue(String key, int scope) {

		if (super.isFinalValueKey(key)) {
			return key.substring(1, key.length() - 1);
		}

		if (super.isExpression(key)) {

			if (SATURN_CONTEXT.equals(key)) {
				return this;
			}

			if (SATURN_SYSTEM_DATE.equals(key)) {
				return DateUtils.getSystemDate();
			}

			if (SATURN_SYSTEM_TIME.equals(key)) {
				return DateUtils.getSystemTime();
			}
			return super.getExpression(key);
		}
		Object value = null;

		switch (scope) {
		case IParameterInfo.SCOPE_LOGIC: {
			value = this.logicMap.get(key);

			if (value != null) {
				return value;
			}
			// break
		}
		case IParameterInfo.SCOPE_REQUEST: {
			String[] paramValues = this.request.getParameterValues(key);

			if (paramValues != null) {

				if (paramValues.length == 1) {
					return paramValues[0];

				} else {
					// List<String> paramList = new ArrayList<String>(
					// paramValues.length);
					StringBuffer paramList = new StringBuffer();

					for (String paramValue : paramValues) {
						paramList.append("," + paramValue);
					}
					paramList.deleteCharAt(0);
					return paramList.toString();
				}
			}

			Object param = this.request.getAttribute(key);
			if (param != null) {
				return param;
			}

			// break;
		}
		case IParameterInfo.SCOPE_SESSION: {
			value = this.request.getSession().getAttribute(key);

			if (value != null) {
				return value;
			}
			// break;
		}
		case IParameterInfo.SCOPE_APPLICATOIN: {
			value = this.request.getSession().getServletContext().getAttribute(
					key);

			if (value != null) {
				return value;
			}
			// break;
		}
		default: {
			return value;
		}
		}
	}

	public void setValue(String key, Object value, int scope) {
		switch (scope) {
		case IParameterInfo.SCOPE_LOGIC: {
			this.logicMap.put(key, value);
			break;
		}
		case IParameterInfo.SCOPE_REQUEST: {
			this.request.setAttribute(key, value);
			break;
		}
		case IParameterInfo.SCOPE_SESSION: {
			this.request.getSession().setAttribute(key, value);
			break;
		}
		case IParameterInfo.SCOPE_APPLICATOIN: {
			this.request.getSession().getServletContext().setAttribute(key,
					value);
			break;
		}
		}
	}

	@Override
	public JexlContext getJexlContext() {
		return this.context;
	}

	@SuppressWarnings("unchecked")
	@Override
	public Set<String> getValueKeys(int scope) {
		switch (scope) {
		case IParameterInfo.SCOPE_LOGIC: {
			return this.logicMap.keySet();
		}
		case IParameterInfo.SCOPE_REQUEST: {
			Set<String> keys = new HashSet<String>();
			keys.addAll(this.request.getParameterMap().keySet());
			Enumeration<String> attributeKeys = this.request
					.getAttributeNames();

			keys.addAll(this.getKeysfromEnumeration(attributeKeys));
			return keys;
		}
		case IParameterInfo.SCOPE_SESSION: {
			Enumeration<String> attributeKeys = this.request.getSession()
					.getAttributeNames();

			return this.getKeysfromEnumeration(attributeKeys);
		}
		case IParameterInfo.SCOPE_APPLICATOIN: {
			Enumeration<String> attributeKeys = this.request.getSession()
					.getServletContext().getAttributeNames();

			return this.getKeysfromEnumeration(attributeKeys);
		}
		}
		return new HashSet<String>();
	}

	@SuppressWarnings("unused")
	private Set<String> getKeysfromEnumeration(Enumeration<String> enumeration) {
		Set<String> keys = new HashSet<String>();

		while (enumeration.hasMoreElements()) {
			keys.add(enumeration.nextElement());
		}
		return keys;
	}

	@SuppressWarnings("unchecked")
	private void initalLogicMapByWebContext(HttpServletRequest request) {
		this.logicMap.put("request", this.request);
		this.logicMap.put("response", this.response);
		Enumeration<String> requestParameterKeys = request.getParameterNames();

		while (requestParameterKeys.hasMoreElements()) {
			String key = requestParameterKeys.nextElement();
			String[] values = request.getParameterValues(key);

			if (values != null) {

				if (values.length == 1) {
					this.logicMap.put(key, values[0]);

				} else if (values.length > 1) {
					List<String> valueList = new ArrayList<String>();

					for (int i = 0; i < values.length; i++) {
						valueList.add(values[i]);
					}
					this.logicMap.put(key, valueList);
				}
			}
		}
		Enumeration<String> requestKeys = request.getAttributeNames();

		while (requestKeys.hasMoreElements()) {
			String key = requestKeys.nextElement();
			Object value = request.getAttribute(key);

			this.logicMap.put(key, value);
		}

		Enumeration<String> sessionKeys = request.getSession()
				.getAttributeNames();

		while (sessionKeys.hasMoreElements()) {
			String key = sessionKeys.nextElement();
			Object value = request.getSession().getAttribute(key);

			this.logicMap.put(key, value);
		}

		Enumeration<String> applicationKeys = request.getSession()
				.getServletContext().getAttributeNames();

		while (applicationKeys.hasMoreElements()) {
			String key = applicationKeys.nextElement();
			Object value = request.getSession().getServletContext()
					.getAttribute(key);

			this.logicMap.put(key, value);
		}
	}

	@SuppressWarnings("unchecked")
	public void cleanScope(int scope) {
		switch (scope) {
		case IParameterInfo.SCOPE_LOGIC: {
			this.logicMap.clear();
		}
		case IParameterInfo.SCOPE_REQUEST: {
			this.request.getParameterMap().clear();
			Enumeration<String> attributeKeys = this.request
					.getAttributeNames();

			this.removeAttributesfromEnumeration(attributeKeys,
					IParameterInfo.SCOPE_REQUEST);
		}
		case IParameterInfo.SCOPE_SESSION: {
			Enumeration<String> attributeKeys = this.request.getSession()
					.getAttributeNames();

			this.removeAttributesfromEnumeration(attributeKeys,
					IParameterInfo.SCOPE_SESSION);
		}
		case IParameterInfo.SCOPE_APPLICATOIN: {
			Enumeration<String> attributeKeys = this.request.getSession()
					.getServletContext().getAttributeNames();

			this.removeAttributesfromEnumeration(attributeKeys,
					IParameterInfo.SCOPE_APPLICATOIN);
		}
		}
	}

	private void removeAttributesfromEnumeration(
			Enumeration<String> attributeKeys, int scope) {

		switch (scope) {
		case IParameterInfo.SCOPE_REQUEST: {

			while (attributeKeys.hasMoreElements()) {
				this.request.removeAttribute(attributeKeys.nextElement());
			}
			break;
		}
		case IParameterInfo.SCOPE_SESSION: {

			while (attributeKeys.hasMoreElements()) {
				this.request.getSession().removeAttribute(
						attributeKeys.nextElement());
			}
			break;
		}
		case IParameterInfo.SCOPE_APPLICATOIN: {

			while (attributeKeys.hasMoreElements()) {
				this.request.getSession().getServletContext().removeAttribute(
						attributeKeys.nextElement());
			}
			break;
		}
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public void removeParameterKey(String key, int scope) {

		if (key != null && !"".equals(key)) {

			switch (scope) {
			case IParameterInfo.SCOPE_REQUEST: {
				Set<String> keys = this.request.getParameterMap().keySet();

				if (keys.contains(key)) {
					this.request.getParameterMap().remove(key);
				}
				this.request.removeAttribute(key);
				break;
			}
			case IParameterInfo.SCOPE_SESSION: {
				this.request.getSession().removeAttribute(key);
				break;
			}
			case IParameterInfo.SCOPE_APPLICATOIN: {
				this.request.getSession().getServletContext().removeAttribute(
						key);

				break;
			}
			}
		}
	}
	
	public ILogicContext newLogicContext() {
		MockRequest mockRequest = new MockRequest(this.request);
		MockResponse mockResponse = new MockResponse();
		
		
		SaturnActionContext context = new SaturnActionContext(super.getId(), mockRequest, mockResponse);
		
		for (String key : this.logicMap.keySet()) {
			context.logicMap.put(key, this.logicMap.get(key));
		}
		
		return context;
	}
}
